#include <BDuerSDK.h>
#include <iostream>
#include <fcntl.h>
#include <cstring>
#include <stdio.h>
#include <stdlib.h>
#include "alsa/asoundlib.h"
#include "WaveCore.h"
#include "vector"
using namespace std;
using namespace bduer;
using namespace mmjd;
mmjd::yuyin::WaveCore core;

#define YUYIN_RECOGNIZING 1
#define YUYIN_HECHENG     2
bool initflag = false;

unsigned char command ;
typedef struct WaveObj
{
    std::vector<short> data;
    unsigned int    length;
    unsigned int    simple_rate;
    float           cur_time;
    unsigned long   frequency;
}WaveObj_t;
WaveObj_t wave;
snd_pcm_t *handle;
int sample = 16000;
float sampletime = 0.1;
bool voice_flag = false;
int none_voice_number = 0;

int datalength = sample * sampletime;
BDuerSDK *duerSDK;
void* push_audio_test(void* args);

void audio_stream(short* stream, unsigned int streamSize);
void push_audio_stream(char* stream, unsigned int streamSize);
class ReqBack : public IBDuerSDKCallback {
public:
    ReqBack() {

    }

    ~ReqBack() {

    }

    void remote_callback(BDuerMessage& message) {
    }

    void message_callback(BDuerMessage& message) {
        if (strcmp(BDUER_CALLBACK_REGISTER_SUCCESS, message.get_name()) == 0) {
            cout << "注册成功" << endl;
            int req;
            BDuerMessage message;
            message.set_name(BDUER_CMD_START);
            message.set_parameter(BDUER_PARAM_KEY_SPEECH_ENGINE_SUPPORT, SPEECH_SUPPORT_WAKEUP_AND_ASR);
            message.set_parameter(BDUER_PARAM_KEY_AFTER_WAKE_UP, AFTER_FINISH_WAITING);
            duerSDK->post(message, &req);

            pthread_t pid;
            pthread_create(&pid, NULL, push_audio_test, NULL);

        } else if (strcmp(BDUER_CALLBACK_ASR, message.get_name()) == 0) {
            int status;
            message.get_parameter(BDUER_PARAM_KEY_ASR_STATUS, status);
            char* word;
            int len;

            switch (status) {
            case ASRStatusStart: {
                cout << "识别开始:" << endl;
                break;
            }

            case ASRStatusVadEnd: {
                break;
            }

            case ASRStatusFlushData: {
                message.get_parameter(BDUER_PARAM_KEY_ASR_DATA, word, len);
                cout << "中间识别结果:%s" << word << endl;
                break;
            }

            case ASRStatusFinish: {
                message.get_parameter(BDUER_PARAM_KEY_ASR_DATA, word, len);
                cout << "识别结果:%s" << word << endl;
                break;
            }

            case ASRStatusCancel: {
                cout << "识别取消" << endl;

                break;
            }

            case ASRStatusMeterLevel: {
                int volume;
                message.get_parameter(BDUER_PARAM_KEY_ASR_METER_LEVEL, volume);
                //cout << "音量为:%d" << volume << endl;
                break;
            }

            default:
                ;
            }
        } else if (strcmp(BDUER_CALLBACK_TTS, message.get_name()) == 0) {
            int status;
            message.get_parameter(BDUER_PARAM_KEY_TTS_STATUS, status);

            switch (status) {
            case TTSStatusBegin: {
                cout << "语音合成开始:" << endl;
                break;
            }

            case TTSStatusAudioData: {
                /**
                * char *stream　是返回的tts数据
                * int length    是返回的tts的数据长度
                */
                char* stream;
                int length;
                message.get_parameter(BDUER_PARAM_KEY_TTS_AUDIO_DATA, stream, length);
                break;
            }

            case TTSStatusFinish: {
                cout << "语音合成结束..." << endl;
                break;
            }

            default:
                ;
            }
        } else if (strcmp(BDUER_CALLBACK_DUER, message.get_name()) == 0) {
            char* content;
            int len;
            message.get_parameter(BDUER_PARAM_KEY_DUER_SERVER_CONTENT, content, len);
        } else if (strcmp(BDUER_CALLBACK_HTTP_RESPONSE, message.get_name()) == 0) {
            char* content;
            int length;
            message.get_parameter(BDUER_PARAM_KEY_HTTP_RESPONSE, content, length);
        } else if (strcmp(BDUER_CALLBACK_WAKEUP, message.get_name()) == 0) {
            int status;
            message.get_parameter(BDUER_PARAM_KEY_WAKEUP_STATUS, status);

            switch (status) {
            case WakeupStatusTriggered: {
                int req;
                printf("唤醒成功");
                //进入识别模式
                BDuerMessage message;
                message.set_name((char*) BDUER_CMD_OPEN_SPEECH_ENGINE);
                message.set_parameter(BDUER_PARAM_KEY_OPEN_SPEECH_ENGINE, SPEECH_ENGINE_ASR);
                duerSDK->post(message, &req);

                break;
            }

            default:
                break;
            }
        } else if (strcmp(BDUER_CALLBACK_FINAL, message.get_name()) == 0) {
            char* errorMsg;
            int len;
            int errorCode;
            message.get_parameter(BDUER_PARAM_KEY_ERROR_CODE, errorCode);
            message.get_parameter(BDUER_PARAM_KEY_ERROR_DESC, errorMsg, len);

            switch (errorCode) {
            case BDuerTTSErrorCode :
                cout << "ERROR CODE: BDuerTTSErrorCode, Error MSG:%s" << errorMsg << endl;
                break;

            case BDuerSDKFinishCode:
                cout << " BDuerSDKFinishCode " << endl;
                break;

            case BDuerRegisterErrorCode:
                cout << "ERROR CODE: BDuerRegisterErrorCode, Error MSG:%s" << errorMsg << endl;
                break;

            case BDuerWakeupErrorCode:
                cout << "ERROR CODE: BDuerWakeupErrorCode, Error MSG:%s" << errorMsg << endl;
                break;

            case BDuerASRErrorCode:
                cout << "ERROR CODE: BDuerASRErrorCode, Error MSG:%s" << errorMsg << endl;
                break;

            case BDuerSDKErrorCode:
                cout << "ERROR CODE: BDuerSDKErrorCode, Error MSG:%s" << errorMsg << endl;
                break;

            case BDuerDUMIServerErrorCode:
                cout << "ERROR CODE: BDuerDUMIServerErrorCode, Error MSG:%s" << errorMsg << endl;
                break;
            }
        }
    };
};

void* push_audio_test(void* args) {
    char audioBuf[4000] = {0};
    FILE* fp = fopen("./record.pcm", "rb");

    if (fp != NULL) {

        while (!feof(fp)) {
            size_t readCount = fread(audioBuf, 1, 4000, fp);

            //cout << "DataRead: " << readCount << endl;
            if (readCount > 0) {

                push_audio_stream(audioBuf, (int)readCount);

                usleep(125000); // 4000 bytes is 0.125 seconds of audio, sleep 0.125 seconds to simulate actual conditions if recording
            }
        }

        fclose(fp);
        sleep(5);
    } else {
        cout << "Failed open audio file" << endl;
    }
}

//用于推送录音数据
void push_audio_stream(char* stream, unsigned int streamSize) {

    int req;
    BDuerMessage message;
    message.set_name((char*) BDUER_CMD_AUDIORECORDER_PCM_STREAM_BUFFER);
    message.set_parameter(BDUER_PARAM_KEY_AUDIORECORDER_PCM_STREAM_DATA, (const char*) stream,
                          streamSize);
    duerSDK->post(message, &req);
}
ReqBack *reqBack = new ReqBack();
BDuerMessage message;
bool audio_wakeup()
{ //小度你好
    int req;
    short *stream;
    unsigned int streamSize;
    FILE *fp;
    fp= NULL;
    char * filename;
    filename = "wakeup.pcm";
    fp = fopen(filename, "r");
    if (NULL == fp)
    {
        std:cout << "open file failed ..." << std::endl;
    }
    fseek(fp, 0, SEEK_END);
    int content_len;
    content_len = ftell(fp);
    fseek(fp, 0, SEEK_SET);
    short *audiodata ;
    audiodata = (short *)malloc(content_len);
    fread(audiodata, content_len, sizeof(char), fp);
    audio_stream(audiodata, content_len);
    message.set_name((char *) BDUER_CMD_START_SPEECH_ENGINE);
    message.set_parameter(BDUER_PARAM_KEY_AUDIORECORDER_PCM_STREAM_DATA, (const char *) stream, streamSize);
    duerSDK->post(message, &req);
    if(req == BDUER_POST_SUCCESS)
    {
        std::cout<<"wake up successful >>>>>>>>>>>>>>>!"<<std::endl;
    }
}
//用于推送录音数据
void audio_stream(short *stream, unsigned int streamSize) {
    // cout << "audioStreamCallback:" << streamSize << endl;
    //fwrite(stream, 1, streamSize, recordFp);
    int req;
    message.set_name((char *) BDUER_CMD_START_SPEECH_ENGINE);
    message.set_parameter(BDUER_PARAM_KEY_AUDIORECORDER_PCM_STREAM_DATA, (const char *) stream, streamSize);
    duerSDK->post(message, &req);
    if(req == BDUER_POST_SUCCESS)
    {
        std::cout<<"post success >>>>>>>>>>>>>>>!"<<std::endl;

    }
}
bool initAudioDev(snd_pcm_t * &handle,snd_pcm_stream_t stream,unsigned int sample,float sampletime)
{
    //声卡设置z
    int rc;
    snd_pcm_hw_params_t *params;
    unsigned int val;
    int dir;
    snd_pcm_uframes_t frames;
    /* Open PCM device for recording (capture). SND_PCM_STREAM_CAPTURE */
    rc = snd_pcm_open(&handle, "plughw:1,0",  stream, 0);
    if (rc < 0)
    {
        fprintf(stderr,  "unable to open pcm device: %s/n",  snd_strerror(rc));
        exit(1);
    }
    /* Allocate a hardware parameters object. */
    snd_pcm_hw_params_alloca(&params);
    /* Fill it in with default values. */
    snd_pcm_hw_params_any(handle, params);
    /* Set the desired hardware parameters. */
    /* Interleaved mode */
    snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
    /* Signed 16-bit little-endian format */
    snd_pcm_hw_params_set_format(handle, params,  SND_PCM_FORMAT_S16_LE);
    /* Two channels (stereo) */
    snd_pcm_hw_params_set_channels(handle, params, 1);
    /* 44100 bits/second sampling rate (CD quality) */
    val = sample;
    snd_pcm_hw_params_set_rate_near(handle, params,  &val, &dir);
    /* Set period size to 32 frames. */
    frames = val*sampletime; //每次读时间 (毫秒）
    snd_pcm_hw_params_set_period_size_near(handle,  params, &frames, &dir);
    /* Write the parameters to the driver */
    rc = snd_pcm_hw_params(handle, params);
    if (rc < 0)
    {
        fprintf(stderr,  "unable to set hw parameters: %s/n", snd_strerror(rc));
        exit(1);
    }
    /* Use a buffer large enough to hold one period */
    snd_pcm_hw_params_get_period_size(params,  &frames, &dir);
    /* We want to loop for 5 ms */
    snd_pcm_hw_params_get_period_time(params,  &val, &dir);
    //参数设置完毕
    return 1;
}
bool listenAudio(WaveObj_t &wave,snd_pcm_t * &handle,unsigned sample,float sampletime,bool onoff)
{
    int requiredLen = sample * sampletime; //44100*0.025
    //char *buffer;
    short *buffer;
    unsigned int showwavelength = 0;
    bool returnflag = false;
    buffer = (short *) malloc(requiredLen*2);
    snd_pcm_prepare(handle);
    int rc = snd_pcm_readi(handle, buffer, requiredLen);
    if (rc == -EPIPE)
    {
        printf("overrun occurred \n");
        snd_pcm_prepare(handle);
        returnflag = false;
    }
    if (rc < 0)
    {
        returnflag = false;
    }
    if (rc != (int)requiredLen)
    {
        fprintf(stderr, "short read, read %d frames\n", rc);
        returnflag = false;
    }else{
        memcpy(&wave.data[0], buffer, rc*2);
        showwavelength = showwavelength + rc;
        wave.length = showwavelength;
        returnflag = true;
    }
    free(buffer);
    return returnflag;
}

bool closeAudio(snd_pcm_t * &handle)
{
    snd_pcm_drain(handle);
    snd_pcm_close(handle);
}

int main(int argc, char **argv) {
//    testPlay();
    //进程接收消息
    bool audioInitFlag = false;

    audioInitFlag = initAudioDev(handle,SND_PCM_STREAM_CAPTURE,16000,0.1);
    duerSDK = BDuerSDK::get_instance();
    int req;

    duerSDK->set_event_lisner(reqBack);
    const string id = "123";
    req = duerSDK->init("dmEE8E23619C59414B", "C3491F1A9DB84A84918C032EDCFCC012","test");

    short *stream = NULL;
    unsigned int streamSize;
    //command = 1;
    command = 2;
    while (1) {
        if(audioInitFlag == true)
        {
            cout<< "read Audio data ............................"<<endl;
            listenAudio(wave,handle,sample,sampletime,false);
            bool voice = core.isVoise(wave.data,datalength);
            if (voice) {
                voice_flag = true;
                none_voice_number = 0;
                printf("is voice!\n");
            } else {
                printf("not voice!\n");
                none_voice_number++;
            }

        }
        struct timeval wait = {0, 10000 * 1000};
        ::select(0, NULL, NULL, NULL, &wait);
        if(initflag == true)
        {
            bool wakeupflag ;
            wakeupflag = true;

            switch (command)
            {
                case 1 :

                    if(wakeupflag == true)
                    {
                        FILE *fp;
                        fp= NULL;
                        char * filename;
                        filename = "test.pcm";
                        fp = fopen(filename, "r");
                        if (NULL == fp)
                        {
                            std:cout << "open file failed ..." << std::endl;
                        }
                        fseek(fp, 0, SEEK_END);
                        int content_len;
                        content_len = ftell(fp);
                        fseek(fp, 0, SEEK_SET);
                        short *audiodata ;
                        audiodata = (short *)malloc(content_len);
                        fread(audiodata, content_len, sizeof(char), fp);
                        for(int k = 0;k<content_len;k++)
                        {
                            //std::cout<< audiodata[k]<<std::endl;
                        }
                        audio_stream(audiodata, content_len);
                    }else{
                        std::cout << "wakeup fault ..."<<std::endl;
                    }

                    break;
                case 2 :
                    char * heChengBuffer;
                    heChengBuffer = "你好北京！福玛特机器人欢迎您！";
                    message.set_name((char *) BDUER_CMD_TTS_STR);
                    message.set_parameter(BDUER_PARAM_KEY_TTS_STR, heChengBuffer, sizeof(heChengBuffer));
                    duerSDK->post(message, &req);
                    if(req == BDUER_POST_SUCCESS)
                    {
                        std::cout<<"post success >>>>>>>>>>>>>>>!"<<std::endl;

                    }
                    break;
                default:break;
            }

        }
       std::cout << "go loop ......"<<std::endl;
    }
}



// dmEE8E23619C59414B

